#include <hacks/base/ev_loop.h>

namespace hacks {

    typedef ev_buddy<ev_loop> ev_loop_buddy;

    ev_loop::ev_loop(std::size_t bf_sz) :
            loop_status_(evl_init),
            raw_loop_(nullptr),
            raw_async_(nullptr),
            lock_(),
            pendding_actions_(),
            run_in_thread_id_(),
            rw_buffer_(bf_sz) {
        raw_loop_ = new uv_loop_t;
        uv_loop_init(raw_loop_);
        raw_async_ = new uv_async_t;
        uv_async_init(raw_loop_, raw_async_, &ev_loop::event_loop_async_callback);
    }


    ev_loop::~ev_loop() {
        assert(loop_status_ == evl_closed || loop_status_ == evl_init);
        delete raw_loop_;
        delete raw_async_;
    }

    void ev_loop::run_loop(run_mode mode) {
        loop_status_.store(evl_running, std::memory_order_release);
        run_in_thread_id_ = std::this_thread::get_id();
        //alloc buddy
        auto *buddy = new ev_loop_buddy;
        buddy->shd_ins = shared_from_this();
        assert(buddy->shd_ins);
        raw_loop_->data = buddy;
        raw_async_->data = buddy;
        //
        uv_run(raw_loop_, static_cast<uv_run_mode>(mode));
        while (uv_loop_close(raw_loop_) != 0) {}
        loop_status_.store(evl_closed, std::memory_order_release);
        //free buddy
        delete buddy;
    }

    void ev_loop::stop_loop() {
        if (in_event_loop()) {
            if (loop_status_ != evl_running) {
                return;
            }
            loop_status_.store(evl_closing, std::memory_order_release);
            uv_close(reinterpret_cast<uv_handle_t *>(raw_async_), &ev_loop::event_loop_handlers_close_callback);
        } else {
            run_in_loop([loop = shared_from_this()]() {
                assert(loop);
                loop->stop_loop();
            });
        }
    }

    int ev_loop::run_in_loop(hacks::ev_loop_functor &&ae) {
        assert(ae);
        if (loop_status_ == evl_closing ||
            loop_status_ == evl_closed ||
            uv_is_closing(reinterpret_cast<uv_handle_t *>(raw_async_))) {
            return -1;
        }
        if (in_event_loop()) {
            ae();
        } else {
            hacks::spin_lock_guard guard(lock_);
            pendding_actions_.emplace_back(std::forward<hacks::ev_loop_functor>(ae));
            uv_async_send(raw_async_);
        }
        return 0;
    }

    bool ev_loop::in_event_loop() const {
        return std::this_thread::get_id() == run_in_thread_id_;
    }

    event_loop_status ev_loop::get_loop_status() const {
        return static_cast<event_loop_status>(loop_status_.load(std::memory_order_acquire));
    }

    uv_loop_t *ev_loop::get_raw_loop() const {
        return raw_loop_;
    }

    void ev_loop::event_loop_async_callback(uv_async_t *async) {
        auto *buddy = static_cast<ev_loop_buddy *> (async->data);
        auto evtloop = buddy->shd_ins;
        assert(evtloop != nullptr);
        auto &pendding_actioons = evtloop->pendding_actions_;
        auto shd_loop_self = evtloop->shared_from_this();
        while (!pendding_actioons.empty()) {
            auto &item_action = pendding_actioons.front();
            if (item_action) {
                item_action();
            }
            pendding_actioons.pop_front();
        }
    }

    void ev_loop::event_loop_handlers_close_callback(uv_handle_t *handler) {
        //async closed
        auto *buddy = static_cast<ev_loop_buddy *> (handler->data);
        auto evtloop = buddy->shd_ins;
        assert(evtloop != nullptr);
        uv_stop(evtloop->raw_loop_);
    }

    ev_loop_buffer &ev_loop::get_loop_buffer() {
        return rw_buffer_;
    }
}
